/*
 * debugcmd.c
 *
 *  Created on: 2020年2月24日
 *      Author: wangbing
 *      Email : mz8023yt@163.com
 */

#ifdef __cplusplus
extern "C" {
#endif

#include "debugcmd.h"
#include <stdlib.h>
#include <string.h>

#include "cJSON.h"

/**
 * "dlvl" variables must be defined if debug message is to be printed through the dlog/dmsg, and "dlvl" must be declared static
 */
static int dlvl = DINF;

/* 内部函数声明 */
int debugcmd_main_help_handler(void* cmd, char* param);
int debugcmd_sub_help_handler(void* cmd, char* param);
int debugcmd_mcmd_find(const char* name, DEBUGCMD_MCMD** cmd);
int debugcmd_mcmd_insert(DEBUGCMD_MCMD* mcmd);
int debugcmd_scmd_find(DEBUGCMD_MCMD* mcmd, const char* sname, DEBUGCMD_SCMD** scmd);
int debugcmd_scmd_insert(DEBUGCMD_MCMD* mcmd, DEBUGCMD_SCMD* scmd);

/* 字符串支持函数 */
int string_find_prefix(const char* s1, const char* s2, int* length);
int string_find_spaces(char* str, int* length);
int string_spaces_index_get(const char* str, int* start, int* end);

/**
 * @brief 获取链表成员
 */
#define MAZCPNT_TO_MAIN_CMD(x) container_of((x), DEBUGCMD_MCMD, list)
#define MAZCPNT_TO_SUB_CMD(x)  container_of((x), DEBUGCMD_SCMD, list)

/**
 * @brief 调试命令链表头对象
 */
static DEBUGCMD_MCMD g_mazcpnt_cmd = {
	.name = DEBUGCMD_HELP_COMMAND,
	.desc = "list all the main commands.",
	.handler = debugcmd_main_help_handler,
};

/**
 * @brief 调试命令框架初始化
 * @retval int 错误码
 */
int debugcmd_init(void)
{
	list_init(&g_mazcpnt_cmd.list);
	return 0;
}

/**
 * @brief 调试命令框架销毁
 * @retval int 错误码
 */
int debugcmd_deinit(void)
{
	list_init(&g_mazcpnt_cmd.list);
	return 0;
}

/**
 * @brief 注册主命令
 * @param mcmd 主命令
 * @retval int 错误码
 */
int debugcmd_mcmd_register(DEBUGCMD_MCMD* mcmd)
{
	int sindex = 0;
	int eindex = 0;
	int length = 0;
	int ret = 0;
	char* str = NULL;
	DEBUGCMD_MCMD* tmp = NULL;

	/* 检查参数有效性 */
	if (NULL == mcmd) {
		printf("err: %s inval mcmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == mcmd->name) {
		printf("err: %s inval mcmd->name.\n", __func__);
		return MAZRET_EINVAL;
	}

	/* 适配命令关键字前后有多余空格的场景 */
	string_spaces_index_get(mcmd->name, &sindex, &eindex);
	length = eindex - sindex;
	if (length < strlen(mcmd->name)) {
		str = malloc(length);
		strncpy(str, mcmd->name + sindex, length);
		mcmd->name = str;
		dmsg(DIGN, "mcmd: %s\n", mcmd->name);
	}

	/* 判断是否有同名的主命令, 已经有同名称的主命令则直接退出函数并报错 */
	ret = debugcmd_mcmd_find(mcmd->name, &tmp);
	if (MAZRET_ENOCMD != ret) {
		printf("err: the command \"%s\" already exists.\n", mcmd->name);
		return MAZRET_EEXIST;
	}

	/* 添加新的主命令到主链表中 */
	list_init(&mcmd->list);
	ret = debugcmd_mcmd_insert(mcmd);
	if (ret) {
		printf("err: the command \"%s\" register failed.\n", mcmd->name);
		return MAZRET_EEXIST;
	}

	/* 初始化默认子命令(帮助命令) */
	mcmd->cmds.name = DEBUGCMD_HELP_COMMAND;
	mcmd->cmds.desc = "list all the sub commands.";
	mcmd->cmds.handler = debugcmd_sub_help_handler;
	list_init(&mcmd->cmds.list);

	mcmd->registered = 1;

	return 0;
}

/**
 * @brief 注销主命令
 * @param mcmd 主命令
 * @retval int 错误码
 */
int debugcmd_mcmd_unregister(DEBUGCMD_MCMD* mcmd)
{
	/* 检查参数有效性 */
	if (NULL == mcmd) {
		printf("err: %s inval mcmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (1 == mcmd->registered) {
		list_del(&mcmd->list);
		mcmd->registered = 0;
	}

	return 0;
}

/**
 * @brief 注册子命令
 * @param mcmd 指定在哪个主命令下注册子命令
 * @param scmd 子命令
 * @retval int 错误码
 */
int debugcmd_scmd_register(DEBUGCMD_MCMD* mcmd, DEBUGCMD_SCMD* scmd)
{
	int sindex = 0;
	int eindex = 0;
	int length = 0;
	int ret = 0;
	char* str = NULL;
	DEBUGCMD_SCMD* tmp = NULL;

	/* 检查参数有效性 */
	if (NULL == mcmd) {
		printf("err: %s inval mcmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == scmd) {
		printf("err: %s inval scmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == scmd->name) {
		printf("err: %s inval scmd->name.\n", __func__);
		return MAZRET_EINVAL;
	}

	/* 适配命令关键字前后有多余空格的场景 */
	string_spaces_index_get(scmd->name, &sindex, &eindex);
	length = eindex - sindex;
	if (length < strlen(scmd->name)) {
		str = malloc(length);
		strncpy(str, scmd->name + sindex, length);
		scmd->name = str;
		dmsg(DIGN, "scmd: %s\n", scmd->name);
	}

	/* 判断是否有同名的子命令, 已经有同名称的子命令则直接退出函数并报错 */
	ret = debugcmd_scmd_find(mcmd, scmd->name, &tmp);
	if (MAZRET_ENOSUBCMD != ret) {
		printf("err: the command \"%s %s\" already exists.\n", mcmd->name, scmd->name);
		return MAZRET_EEXIST;
	}

	list_init(&scmd->list);
	ret = debugcmd_scmd_insert(mcmd, scmd);
	if (ret) {
		printf("err: the command \"%s %s\" register failed.\n", mcmd->name, scmd->name);
		return MAZRET_EEXIST;
	}

	scmd->registered = 1;

	return 0;
}

/**
 * @brief 注销子命令
 * @param scmd 子命令
 * @retval int 错误码
 */
int debugcmd_scmd_unregister(DEBUGCMD_SCMD* scmd)
{
	/* 检查参数有效性 */
	if (NULL == scmd) {
		printf("err: %s inval mcmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (1 == scmd->registered) {
		list_del(&scmd->list);
		scmd->registered = 0;
	}

	return 0;
}

/**
 * @brief 执行指定命令的回调函数
 * @param str 命令字符串, 包含主子命令和参数
 * @retval int 错误码
 */
int debugcmd_execute(char* str)
{
	int ret = 0;
	char uart_cmd[DEBUGCMD_MAX_STR_LEN];
	char* mname = NULL;
	char* sname = NULL;
	char* param = NULL;
	DEBUGCMD_MCMD* mcmd = NULL;
	DEBUGCMD_SCMD* scmd = NULL;

	/* 检查参数有效性 */
	if (NULL == str) {
		printf("err: %s inval str.\n", __func__);
		return MAZRET_EINVAL;
	}

	memset(uart_cmd, 0, DEBUGCMD_MAX_STR_LEN);
	memcpy(uart_cmd, str, strlen(str));

	/* 解析主命令 */
	mname = strtok_r(uart_cmd, " ", &param);
	if (NULL == mname) {
		return MAZRET_EINVAL;
	}
	if ('\0' == *mname) {
		return MAZRET_EINVAL;
	}

	ret = debugcmd_mcmd_find(mname, &mcmd);
	if (ret) {
		printf("%s: command not found.\n", mname);
		return MAZRET_ENOCMD;
	}

	/* 解析子命令 */
	sname = strtok_r(NULL, " ", &param);
	if (NULL == sname && NULL == mcmd->handler) {
		printf("%s: handler not found.\n", mname);
		return MAZRET_REMOTECMD;
	}

	/* 如果仅输入了一个主命令, 并且主命令回调非空 */
	if (NULL == sname && mcmd->handler != NULL) {
		ret = mcmd->handler(scmd, param);
		if (ret) {
			printf("%s: execute failed.\n", mname);
			return MAZRET_EEXE;
		}
		return 0;
	}

	/* 获取对应的子命令句柄 */
	ret = debugcmd_scmd_find(mcmd, sname, &scmd);
	if (ret) {
		printf("%s: %s: sub command not found.\n", mname, sname);
		return MAZRET_ENOSUBCMD;
	}

	if (NULL == scmd->handler) {
		return MAZRET_REMOTECMD;
	}

	ret = scmd->handler(mcmd, param);
	if (ret) {
		printf("%s %s: execute failed.\n", mname, sname);
		return MAZRET_EEXE;
	}

	return 0;
}

/**
 * @brief 命令自动补全接口
 * @param str 命令字符串, 包含主子命令和参数
 * @param fill 自动填充的字符串, 内存大小必须大于 DEBUGCMD_MAX_STR_LEN
 * @param length 自动填充的长度
 * @retval int 错误码
 */
int debugcmd_automatic_completion(char* str, char* fill, int* length)
{
	int ret = 0;
	char uart_cmd[DEBUGCMD_MAX_STR_LEN]; // 暂存目前已经输入的命令信息
	char* mname;                         // 暂存解析得到的主命令字符串
	char* sname;                         // 暂存解析得到的子命令字符串
	char* param;                         // 暂存剩余未解析的字符串
	DEBUGCMD_MCMD* mcmd;                 // 记录匹配到的主命令
	DEBUGCMD_SCMD* scmd;                 // 记录匹配到的子命令
	DEBUGCMD_MCMD* tmp_mcmd;             // 用于遍历主命令链表的临时变量
	DEBUGCMD_SCMD* tmp_scmd;             // 用于遍历子命令链表的临时变量
	struct list_head* tmp_list;          // 遍历链表的临时变量
	int in_mcmd_len = 0;                 // 输入字符中主命令的长度
	int in_scmd_len = 0;                 // 输入字符中子命令的长度
	int in_len = 0;                      // 输入字符的长度
	int match_count = 0;                 // 记录有多少个包含项
	int in_mspace_len = 0;               // 记录输入主命令字符串前面有多少个空格
	int in_sspace_len = 0;               // 记录输入子主命令字符串前面有多少个空格
	int predix_len = 0;                  // 用于多个命令匹配时记录多个命令共同的前缀长度
	int predix_len_tmp = 0;              // 用于多个命令匹配时记录多个命令共同的前缀长度, 临时暂存

	/* 检查参数有效性 */
	if (NULL == str) {
		printf("err: %s inval str.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == fill) {
		printf("err: %s inval fill.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == length) {
		printf("err: %s inval length.\n", __func__);
		return MAZRET_EINVAL;
	}

	/* fill 字符串内存大小必须大于 DEBUGCMD_MAX_STR_LEN */
	memset(fill, 0, DEBUGCMD_MAX_STR_LEN);
	*length = 0;

	/* 暂存目前已经输入的命令信息 */
	memset(uart_cmd, 0, DEBUGCMD_MAX_STR_LEN);
	memcpy(uart_cmd, str, strlen(str));

	/* 解析主命令 */
	mname = strtok_r(uart_cmd, " ", &param);
	if (NULL == mname) {
		printf("err: strtok main cmd failed.\n");
		return MAZRET_ENULLP;
	}

	string_find_spaces(str, &in_mspace_len);
	in_len = strlen(str);
	in_mcmd_len = strlen(mname);

	/* 尝试解析子命令 */
	sname = strtok_r(NULL, " ", &param);

	/* 解析结果有两种情况
	 * 1. 仅有主命令
	 * 2. 既有主命令又有子命令
	 */
	if (NULL == sname) {
		/* 仅有主命令, 有下面几种场景:
		 * 1. 末尾无多余空格
		 *      1. 主命令完全, 无多余空格, 仅有一个匹配项 ---> 加一个空格, 并列举子命令
		 *      2. 主命令不全, 无多余空格, 仅有一个匹配项 ---> 直接补全主命令, 并追加一个空格
		 *      3. 主命令完全, 无多余空格, 且有多个匹配项 ---> 列举所有匹配项目
		 *      4. 主命令不全, 无多余空格, 且有多个匹配项 ---> 补全到差异位置, 并列举所有匹配项目
		 *      5. 主命令错误, 无多余空格  ---> 输出提示音
		 * 2.末尾有多余空格, 则默认主命令完全
		 *      6. 有匹配项 ---> 列举子命令
		 *      7. 无匹配项 ---> 输出提示音
		 */

		if (in_len == (in_mcmd_len + in_mspace_len)) {
			/* 末尾无多余空格走此流程 */
			match_count = 0;

			/* 先遍历一遍所有主命令, 看有几个包含项 */
			tmp_mcmd = &g_mazcpnt_cmd;
			tmp_list = tmp_mcmd->list.next;
			tmp_mcmd = MAZCPNT_TO_MAIN_CMD(tmp_list);

			while (strcmp(tmp_mcmd->name, DEBUGCMD_HELP_COMMAND)) {
				if (!strncmp(tmp_mcmd->name, mname, in_mcmd_len)) {
					match_count++;
					if (1 == match_count) {
						/* 第一个匹配的命令, 将其记录下来 */
						mcmd = tmp_mcmd;
					} else if (2 == match_count) {
						/* 由于第一个匹配项没有打印, 在第二个匹配项的时候补打印 */
						printf("\r\n%s%s", mcmd->name, DEBUGCMD_SEPARATOR);
						printf("%s%s", tmp_mcmd->name, DEBUGCMD_SEPARATOR);
						string_find_prefix(mcmd->name, tmp_mcmd->name, &predix_len);
					} else {
						/* 再后面的匹配项直接打印就好 */
						printf("%s%s", tmp_mcmd->name, DEBUGCMD_SEPARATOR);
						string_find_prefix(mcmd->name, tmp_mcmd->name, &predix_len_tmp);
						predix_len = predix_len < predix_len_tmp ? predix_len : predix_len_tmp;
					}
				}

				tmp_list = tmp_mcmd->list.next;
				tmp_mcmd = MAZCPNT_TO_MAIN_CMD(tmp_list);
			}

			if (0 == match_count) {
				/* 场景5: 主命令错误, 无多余空格  ---> 输出提示音 */
				*length = 0;
				return MAZRET_ERING;
			} else if (1 == match_count) {
				/* 有一个包含项, 对应场景1和2 */
				if (!strcmp(mcmd->name, mname)) {
					/* 场景1: 主命令完全, 无多余空格, 仅有一个匹配项 ---> 列举子命令*/
					printf("\r\n");
					ret = mcmd->cmds.handler(mcmd, NULL);
					if (ret) {
						printf("err: help sub cmd handler failed.\n");
						return MAZRET_EEXE;
					}
					*length = 1;
					*fill = ' ';
					return MAZRET_ENEWLINE;
				} else {
					/* 场景2: 主命令不全, 无多余空格, 仅有一个匹配项 ---> 直接补全主命令 */
					*length = strlen(mcmd->name) - in_mcmd_len;
					memcpy(fill, &mcmd->name[in_mcmd_len], *length);
					strcat(fill, " ");
					(*length)++;
					return MAZRET_EAUTO;
				}
			} else {
				/* 有多个包含项, 对应场景3和4 */
				printf("\r\n");
				*length = predix_len - in_mcmd_len;
				if (*length != 0) {
					memcpy(fill, &mcmd->name[in_mcmd_len], *length);
				}
				return MAZRET_ENEWLINE;
			}
		} else {
			/* 如果末尾有空格, 则主命令必须是正确的 */
			ret = debugcmd_mcmd_find(mname, &mcmd);
			if (ret != 0) {
				/* 场景7: 无匹配项 ---> 不做任何操作 */
				*length = 0;
				return MAZRET_ERING;
			}

			/* 场景6: 有匹配项 ---> 列举子命令 */
			printf("\r\n");
			ret = mcmd->cmds.handler(mcmd, NULL);
			if (ret) {
				printf("err: help sub cmd handler failed.\n");
				return MAZRET_EEXE;
			}
			*length = 0;
			return MAZRET_ENEWLINE;
		}
	} else {
		/* 如果既有主命令又有子命令, 则主命令必须是正确的 */
		ret = debugcmd_mcmd_find(mname, &mcmd);
		if (ret != 0) {
			*length = 0;
			return MAZRET_ERING;
		}

		/* 主命令正确则继续处理子命令, 子命令也有下面几种场景:
		 * 1. 末尾无多余空格
		 *      1. 子命令完全, 无多余空格, 仅有一个匹配项 ---> 加一个空格, 表示已经正确匹配上了
		 *      2. 子命令不全, 无多余空格, 仅有一个匹配项 ---> 补全子命令, 并追加一个空格
		 *      3. 子命令完全, 无多余空格, 且有多个匹配项 ---> 列举所有匹配的子命令
		 *      4. 子命令不全, 无多余空格, 且有多个匹配项 ---> 补全到差异位置, 并列举所有匹配项目
		 *      5. 子命令错误, 无多余空格  ---> 输出提示音
		 * 2. 末尾有多余空格, 则默认主命令完全
		 *      6. 有匹配项 ---> 不做任何操作
		 *      7. 无匹配项 ---> 输出提示音
		 */
		string_find_spaces(str + in_mspace_len + in_mcmd_len, &in_sspace_len);
		in_scmd_len = strlen(sname);

		if (in_len == (in_mspace_len + in_mcmd_len + in_sspace_len + in_scmd_len)) {
			/* 子命令后无多余字符将走到这 */
			match_count = 0;

			/* 先遍历一遍所有主命令, 看有几个包含项 */
			tmp_scmd = &mcmd->cmds;
			tmp_list = tmp_scmd->list.next;
			tmp_scmd = MAZCPNT_TO_SUB_CMD(tmp_list);

			while (strcmp(tmp_scmd->name, DEBUGCMD_HELP_COMMAND)) {
				if (!strncmp(tmp_scmd->name, sname, in_scmd_len)) {
					match_count++;
					if (1 == match_count) {
						/* 第一个匹配的命令, 将其记录下来 */
						scmd = tmp_scmd;
					} else if (2 == match_count) {
						/* 由于第一个匹配项没有打印, 在第二个匹配项的时候补打印 */
						printf("\r\n%s%s", scmd->name, DEBUGCMD_SEPARATOR);
						printf("%s%s", tmp_scmd->name, DEBUGCMD_SEPARATOR);
						string_find_prefix(scmd->name, tmp_scmd->name, &predix_len);
					} else {
						/* 再后面的匹配项直接打印就好 */
						printf("%s%s", tmp_scmd->name, DEBUGCMD_SEPARATOR);
						string_find_prefix(scmd->name, tmp_scmd->name, &predix_len_tmp);
						predix_len = predix_len < predix_len_tmp ? predix_len : predix_len_tmp;
					}
				}

				tmp_list = tmp_scmd->list.next;
				tmp_scmd = MAZCPNT_TO_SUB_CMD(tmp_list);
			}

			if (0 == match_count) {
				/* 场景5: 子命令错误, 无多余空格  ---> 输出提示音 */
				*length = 0;
				return MAZRET_ERING;
			} else if (1 == match_count) {
				/* 有一个包含项, 对应场景1和2 */
				if (!strcmp(scmd->name, sname)) {
					/* 场景1: 子命令完全, 无多余空格, 仅有一个匹配项 ---> 加一个空格 */
					*length = 1;
					*fill = ' ';
					return MAZRET_EAUTO;
				} else {
					/* 场景2: 子命令不全, 无多余空格, 仅有一个匹配项 ---> 补全子命令, 并追加一个空格 */
					*length = strlen(scmd->name) - in_scmd_len;
					memcpy(fill, &scmd->name[in_scmd_len], *length);
					strcat(fill, " ");
					(*length)++;
					return MAZRET_EAUTO;
				}
			} else {
				/* 有多个包含项, 对应场景3和4 */
				printf("\r\n");
				*length = predix_len - in_scmd_len;
				if (*length != 0) {
					memcpy(fill, &scmd->name[in_scmd_len], *length);
				}
				return MAZRET_ENEWLINE;
			}
		} else {
			/* 如果末尾有空格, 则子命令必须是正确的 */
			ret = debugcmd_scmd_find(mcmd, sname, &scmd);
			if (ret != 0) {
				/* 场景7: 无匹配项 ---> 输出提示音 */
				*length = 0;
				return MAZRET_ERING;
			}

			/* 場景6: 有匹配项 ---> 不做任何操作 */
			*length = 0;
			return MAZRET_EAUTO;
		}
	}

	return 0;
}

/**
 * @brief 导出命令列表到JSON文件中
 * @notes 用于dcmd进程解析JSON文件, 以支持TAB自动补全
 * @param filename JSON文件名
 * @retval 错误码
 */
int debugcmd_export(char* filename)
{
	DEBUGCMD_MCMD* mcmd;
	DEBUGCMD_SCMD* scmd;
	struct list_head* mlist;
	struct list_head* slist;
	cJSON* root = NULL;
	cJSON* mnode = NULL;
	cJSON* snode = NULL;

	char* result;          // JSON 字符串
	int length;            // JSON 字符串的长度
	char* name = filename; // 输出的 JSON 文件名
	FILE* fp;              // 输出的 JSON 文件
	int len;               // 实际写入文件中的数据长度
	cJSON_bool ret = 0;

	root = cJSON_CreateObject();
	if (NULL == root) {
		printf("err: cJSON_CreateObject root\n");
		return MAZRET_NG;
	}

	mcmd = &g_mazcpnt_cmd;
	// printf("%-12s %s\r\n", mcmd->name, mcmd->desc);
	mcmd = MAZCPNT_TO_MAIN_CMD(mcmd->list.next);

	/* 遍历主命令链表, 直到再次获取到帮助命令 */
	while (strcmp(mcmd->name, DEBUGCMD_HELP_COMMAND)) {
		mnode = cJSON_CreateObject();
		if (NULL == mnode) {
			printf("err: cJSON_CreateObject %s\n", mcmd->name);
			cJSON_Delete(root);
			return MAZRET_NG;
		}

		ret = cJSON_AddItemToObject(root, mcmd->name, mnode);
		if (!ret) {
			printf("err: cJSON_AddItemToObject %s\n", mcmd->name);
			cJSON_Delete(root);
			return MAZRET_NG;
		}
		// printf("%-12s %s\r\n", mcmd->name, mcmd->desc);

		scmd = &mcmd->cmds;
		// printf("%s %-12s %s\r\n", mcmd->name, scmd->name, scmd->desc);
		scmd = MAZCPNT_TO_SUB_CMD(scmd->list.next);

		/* 遍历子命令链表, 直到再次获取到帮助命令 */
		while (strcmp(scmd->name, DEBUGCMD_HELP_COMMAND)) {
			snode = cJSON_CreateObject();
			if (NULL == snode) {
				printf("err: cJSON_CreateObject %s\n", scmd->name);
				cJSON_Delete(root);
				return MAZRET_NG;
			}

			cJSON_AddStringToObject(snode, "desc", scmd->desc);
			ret = cJSON_AddItemToObject(mnode, scmd->name, snode);
			if (!ret) {
				printf("err: cJSON_AddItemToObject %s\n", scmd->name);
				cJSON_Delete(root);
				return MAZRET_NG;
			}

			// printf("%s %-12s %s\r\n", mcmd->name, scmd->name, scmd->desc);
			slist = scmd->list.next;
			scmd = MAZCPNT_TO_SUB_CMD(slist);
		}

		mlist = mcmd->list.next;
		mcmd = MAZCPNT_TO_MAIN_CMD(mlist);
	}

	result = cJSON_Print(root);
	length = strlen(result);

	/* 创建输出JSON文件 */
	fp = fopen(name, "wb");
	if (!fp) {
		printf("err: open %s file failed!\n", name);
		free(result);
		cJSON_Delete(root);
		return MAZRET_NG;
	}

	len = fwrite(result, 1, length, fp);
	if (len != length) {
		printf("err: %s write failed! len = %d, length = %d\n", name, (int)len, length);
		fclose(fp);
		free(result);
		cJSON_Delete(root);
		return MAZRET_NG;
	}

	fclose(fp);
	free(result);
	cJSON_Delete(root);

	return MAZRET_OK;
}

/**
 * @brief 判断输入字符串是否全是空格
 * @retval int 返回值TRUE或FALSE
 */
int debugcmd_param_only_space(char* str)
{
	int length = 0;

	if (NULL == str) {
		printf("err: %s inval str.\n", __func__);
		return MAZRET_EINVAL;
	}

	length = strlen(str);
	string_find_spaces(str, &length);

	if (strlen(str) != length) {
		return MAZRET_FALSE;
	}

	return MAZRET_TRUE;
}

/**
 * @brief 遍历命令列表, 返回对应的主命令
 * @param name 主命令名称
 * @param cmd 用于返回找到的主命令结构体指针
 * @retval int 错误码
 */
int debugcmd_mcmd_find(const char* name, DEBUGCMD_MCMD** cmd)
{
	DEBUGCMD_MCMD* tmp_cmd = NULL;
	struct list_head* tmp_list = NULL;

	/* 检查参数有效性 */
	if (NULL == name) {
		printf("err: %s inval name.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == cmd) {
		printf("err: %s inval cmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	tmp_cmd = &g_mazcpnt_cmd;

	/* 字符串相等返回0, 退出循环 */
	while (strcmp(tmp_cmd->name, name)) {
		tmp_list = tmp_cmd->list.next;
		tmp_cmd = MAZCPNT_TO_MAIN_CMD(tmp_list);

		if (!strcmp(tmp_cmd->name, DEBUGCMD_HELP_COMMAND)) {
			return MAZRET_ENOCMD;
		}
	}

	*cmd = tmp_cmd;

	return 0;
}

/**
 * @brief 按照字母顺序将新注册的主命令添加到主命令链表中
 * @param mcmd 新注册的主命令
 * @retval int 错误码
 */
int debugcmd_mcmd_insert(DEBUGCMD_MCMD* mcmd)
{
	DEBUGCMD_MCMD* tmp_cmd = NULL;
	struct list_head* tmp_list = NULL;

	/* 检查参数有效性 */
	if (NULL == mcmd) {
		printf("err: %s inval mcmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	tmp_cmd = &g_mazcpnt_cmd;

	/* 只要 mcmd 名称字母排序比当前遍历的更大, 则继续遍历 */
	while (strcasecmp(mcmd->name, tmp_cmd->name) > 0) {
		tmp_list = tmp_cmd->list.next;
		tmp_cmd = MAZCPNT_TO_MAIN_CMD(tmp_list);

		/* 整个链表都遍历结束了, 说明新注册的命令是字母序是最小的 */
		if (!strcmp(tmp_cmd->name, DEBUGCMD_HELP_COMMAND)) {
			/* 直接加到链表的最后面 */
			list_add_tail(&mcmd->list, &g_mazcpnt_cmd.list);
			return 0;
		}
	}

	/* 发现新注册比某一个现有命令的字母序小, 则插到其前面 */
	list_insert(&mcmd->list, tmp_list->prev, tmp_list);
	return 0;
}

/**
 * @brief 遍历命令列表, 查询是否有对应的命令
 * @param mcmd 主命令结构体
 * @param scmd 用于返回找到的子命令结构体指针
 * @retval int 错误码
 */
int debugcmd_scmd_find(DEBUGCMD_MCMD* mcmd, const char* sname, DEBUGCMD_SCMD** scmd)
{
	DEBUGCMD_SCMD* tmp_cmd = NULL;
	struct list_head* tmp_list = NULL;

	/* 检查参数有效性 */
	if (NULL == mcmd) {
		printf("err: %s inval mcmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == sname) {
		printf("err: %s inval sname.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == scmd) {
		printf("err: %s inval scmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	tmp_cmd = &mcmd->cmds;

	/* 字符串相等返回0, 退出循环 */
	while (strcmp(tmp_cmd->name, sname)) {
		tmp_list = tmp_cmd->list.next;
		tmp_cmd = MAZCPNT_TO_SUB_CMD(tmp_list);

		if (!strcmp(tmp_cmd->name, DEBUGCMD_HELP_COMMAND)) {
			return MAZRET_ENOSUBCMD;
		}
	}

	*scmd = tmp_cmd;

	return 0;
}

/**
 * @brief 按照字母顺序将新注册的子命令添加到子命令链表中
 * @param mcmd 指定在哪个主命令下注册子命令
 * @param mcmd 新注册的子命令
 * @retval int 错误码
 */
int debugcmd_scmd_insert(DEBUGCMD_MCMD* mcmd, DEBUGCMD_SCMD* scmd)
{
	DEBUGCMD_SCMD* tmp_cmd;
	struct list_head* tmp_list;

	if (NULL == mcmd) {
		printf("err: %s inval mcmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == scmd) {
		printf("err: %s inval scmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	tmp_cmd = &mcmd->cmds;

	/* 只要 scmd 名称字母排序比当前遍历的更大, 则继续遍历 */
	while (strcasecmp(scmd->name, tmp_cmd->name) > 0) {
		tmp_list = tmp_cmd->list.next;
		tmp_cmd = MAZCPNT_TO_SUB_CMD(tmp_list);

		/* 整个链表都遍历结束了, 说明新注册的命令是字母序是最小的 */
		if (!strcmp(tmp_cmd->name, DEBUGCMD_HELP_COMMAND)) {
			/* 直接加到链表的最后面 */
			list_add_tail(&scmd->list, &mcmd->cmds.list);
			return 0;
		}
	}

	/* 发现新注册比某一个现有命令的字母序小, 则插到其前面 */
	list_insert(&scmd->list, tmp_list->prev, tmp_list);
	return 0;
}

/**
 * @brief 主命令通用回调函数
 * @notes 遍历主命令链表, 打印链表中所有主命令的描述信息
 * @param cmd 主命令结构体
 * @param param 没有使用到, 传NULL即可
 * @retval int 错误码
 */
int debugcmd_main_help_handler(void* cmd, char* param)
{
	DEBUGCMD_MCMD* tmp_cmd;
	struct list_head* tmp_list;

	tmp_cmd = &g_mazcpnt_cmd;
	printf("%-12s %s\r\n", tmp_cmd->name, tmp_cmd->desc);

	tmp_cmd = MAZCPNT_TO_MAIN_CMD(tmp_cmd->list.next);

	/* 字符串相等返回0, 退出循环 */
	while (strcmp(tmp_cmd->name, DEBUGCMD_HELP_COMMAND)) {
		printf("%-12s %s\r\n", tmp_cmd->name, tmp_cmd->desc);
		tmp_list = tmp_cmd->list.next;
		tmp_cmd = MAZCPNT_TO_MAIN_CMD(tmp_list);
	}

	return 0;
}

/**
 * @brief 子命令通用回调函数(默认的help命令回调函数)
 * @notes 遍历子命令链表, 打印链表中所有子命令的描述信息
 * @param cmd 主命令结构体
 * @param param 没有使用到, 传NULL即可
 * @retval int 错误码
 */
int debugcmd_sub_help_handler(void* cmd, char* param)
{
	DEBUGCMD_MCMD* mcmd = NULL;
	DEBUGCMD_SCMD* tmp_cmd = NULL;
	struct list_head* tmp_list = NULL;

	if (NULL == cmd) {
		printf("err: %s inval cmd.\n", __func__);
		return MAZRET_EINVAL;
	}

	mcmd = (DEBUGCMD_MCMD*)cmd;
	tmp_cmd = &mcmd->cmds;
	printf("%s %-12s %s\r\n", mcmd->name, tmp_cmd->name, tmp_cmd->desc);

	tmp_cmd = MAZCPNT_TO_SUB_CMD(tmp_cmd->list.next);

	/* 字符串相等返回0, 退出循环 */
	while (strcmp(tmp_cmd->name, DEBUGCMD_HELP_COMMAND)) {
		printf("%s %-12s %s\r\n", mcmd->name, tmp_cmd->name, tmp_cmd->desc);
		tmp_list = tmp_cmd->list.next;
		tmp_cmd = MAZCPNT_TO_SUB_CMD(tmp_list);
	}

	return 0;
}

/**
 * @brief 找出输入的两个字符串前面相同前缀的长度
 * @param length 返回相同前缀的长度
 * @retval int 返回值为执行成功与否
 */
int string_find_prefix(const char* s1, const char* s2, int* length)
{
	int i = 0;
	int len1 = 0;
	int len2 = 0;
	int len = 0;

	if (NULL == s1) {
		printf("err: %s inval s1.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == s2) {
		printf("err: %s inval s2.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == length) {
		printf("err: %s inval length.", __func__);
		return MAZRET_EINVAL;
	}

	i = 0;
	len1 = strlen(s1);
	len2 = strlen(s2);
	len = len1 < len2 ? len1 : len2; // 取较小的那一个

	for (i = 0; i < len; i++) {
		if (s1[i] != s2[i])
			break;
	}

	*length = i;

	return 0;
}

/**
 * @brief 找出输入的字符串前面空格的长度
 * @param str 输入的字符串
 * @param length 返回空格字符的个数
 * @retval int 返回值为执行成功与否
 */
int string_find_spaces(char* str, int* length)
{
	int i = 0;
	int len = 0;

	if (NULL == str) {
		printf("err: %s inval str.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == length) {
		printf("err: %s inval length.\n", __func__);
		return MAZRET_EINVAL;
	}

	len = strlen(str);

	for (i = 0; i < len; i++) {
		if (str[i] != ' ')
			break;
	}

	*length = i;

	return 0;
}

/**
 * @brief 找出输入的字符串前面和后面多余空格字符的索引值
 * @param str 输入的字符串
 * @param start 第一个非空格字符的索引值
 * @param end 字符串后面第一个空格的索引值
 * @retval int 返回值为执行成功与否
 */
int string_spaces_index_get(const char* str, int* start, int* end)
{
	int i = 0;
	int sindex = 0;
	int eindex = 0;
	int sfound = 0;
	int efound = 0;
	int length = strlen(str);

	if (NULL == str) {
		printf("err: %s inval str.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == start) {
		printf("err: %s inval start.\n", __func__);
		return MAZRET_EINVAL;
	}

	if (NULL == end) {
		printf("err: %s inval end.\n", __func__);
		return MAZRET_EINVAL;
	}

	for (i = 0; i < length; i++) {
		if (str[i] == ' ' && sfound == 0) {
			sindex++;
		}

		if (str[i] != ' ') {
			*start = sindex;
			sfound = 1;
		}

		if (str[i] == ' ' && sfound == 1 && efound == 0) {
			eindex = i;
			*end = eindex;
			efound = 1;
			break;
		}
	}

	if (eindex == 0 && efound == 0) {
		*end = i;
	}

	return 0;
}

#ifdef __cplusplus
}
#endif
